<?php

/**
 * API 方式 - client
 */
class API_Client {

	private $_client;
	private $_secret;
	private $_format = 'JSON';
	private $_url = '';
    private $_host = '';

	public function __construct($client, $secret) {

		$this->_client = $client;
		$this->_secret = $secret;
	}

    /**
     * setUrl
     * 设置 server 链接
     * @param string $url
     */
    public function setUrl($url) {

        $this->_url = $url;
    }

    /**
     * setHost
     * 设置 server HOST
     * @param string $host
     */
    public function setHost($host) {

        $this->_host = $host;
    }

	/**
	 * _initParams
	 * 必要参数初始化
	 *
	 * @param  mixed $method
	 * @return void
	 */
	private function _initParams($method, $args) {

		$params = array();
        $params['log_id'] = Logger::getLogId();
		$params['client'] = $this->_client;
		$params['format'] = strtoupper($this->_format);
		$params['ts'] = V::timestamp();
		$params['method'] = $method;
		$params['args'] = $args;

		return $params;
	}

	/**
	 * _callMethod
	 * 回调方法
	 *
	 * @param  mixed $method
	 * @param  mixed $args
	 * @return void
	 */
	public function callMethod($method, $args = array()) {

		if (!$method) {
			throw new API_Exception('No method given', API_Exception::E_PARAM_ERROR);
		}

		$url = $this->_url;
		$params = $this->_initParams($method, $args);
		$params['sign'] = $this->_generateSign($params);
        $startTime = microtime(true);
		list($errno, $result) = $this->_postRequest($url, $params);
        $costTime = intval(1000 * (microtime(true) - $startTime));
        
        // log
        Logger::setParam('restful_call', array('url' => $url, 'param' => $params, 'errno' => $errno, 'result' => $result, 'cost' => $costTime), true);

		// 对异常情况做处理
		if ($errno) {
			throw new API_Exception('Request error:'.intval($errno), API_Exception::E_LOGIC_ERROR);
		}
        
        return $this->_parseResult($result);
	}
    
    /**
     * 
     * @param type $result
     */
    private function _parseResult($result) {
        
        $data = array();
        switch (strtoupper($this->_format)) {
            case 'JSON':
            default:
                $data = json_decode($result, true);
        }
        return $data;
    }

    /**
	 * _generateSign
	 * sig 生成方法
	 *
	 * @param  mixed $params
	 * @return string
	 */
	private function _generateSign($params) {

		$fields = array('method', 'ts', 'client');
		sort($fields);

		$request = array();
		foreach ($fields as $k) {
			$request[$k] = $params[$k];
		}

		return substr(md5(http_build_query($request, '', '&') . $this->_secret), 8, 16);
	}

	/**
	 * _postRequest
	 *
	 * @param  mixed $url
	 * @param  mixed $data
	 * @return void
	 */
	public function _postRequest($url, $data) {

		if (function_exists('curl_init')) {

			$ch = curl_init();
			curl_setopt($ch, CURLOPT_POST, 1);
			curl_setopt($ch, CURLOPT_URL, $url);
            if ($this->_host) {
                curl_setopt($ch, CURLOPT_HTTPHEADER, array('HOST: ' . $this->_host));
            }
            $data = http_build_query($data);
			curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
			curl_setopt($ch, CURLOPT_USERAGENT, 'API client(curl);phpversion_' . phpversion());
			$result = curl_exec($ch);
			$errno = curl_errno($ch);
			curl_close($ch);

			return array($errno, $result);
		} else {

            $headerHost = '';
            if ($this->_host) {
                $headerHost = 'HOST: ' . $this->_host . "\r\n";
            }

            $data = http_build_query($data);
			$context =
			array('http' =>
					array('method' => 'POST',
						'header' => 'Content-type: application/x-www-form-urlencoded'."\r\n".
									'User-Agent: API client(non-curl);phpversion_'.phpversion()."\r\n".
                                    $headerHost.
									'Content-length: ' . strlen($data),
						'content' => $data));
			$contextid = stream_context_create($context);
			$sock = fopen($url, 'r', false, $contextid);
			if ($sock) {
				$result = '';
				while (!feof($sock)) {
					$result .= fgets($sock, 4096);
				}
				fclose($sock);
			}

			return array(0, $result);
		}
	}
}
